I decided to make a little tip collection for everyone out there who wants to script better.
This isn't a tutorial for newbies because I didn't explain all the commands I've used here; and don't intend to.
If you want to have a script that works better, here are some tips to
improve its performance.
Neat Scripting
The first step is to learn how to write the script correctly and
efficiently.
* Don't use excessive Braces { } where you don't need them, and you only need them when there's more then one line in the command.
The { Brace means "Begin" and the } Brace means "End" so if there's only one line to the event or command, you don't need to give the script a beginning and end command.
For example:
ON *:text:*:*: { if ($nick = Blah) { do something here } }
Which the scrip reads like this: ON *:text:*:*: Begin-event if ($nick = Blah) Begin-if do something here End-if End-event
So as you see, you don't need to tell it where to begin and end if there's only one line.
So this would better:
ON *:text:*:*: if ($nick = Blah) do something here
Now besides the fact that it's useless to put excessive Braces, they all use more CPU.
That's because the { Begin Brace is actually a command that calls a procedure to check for the } End Brace (and run all the commands between of course).
It doesn't take a lot of CPU of course, but it's about the same amount as calling an alias (see "Saving CPU/memory usage").
* If you are making aliases that call stuff so you want have to write a lot of code, then do it right.
Here's an example from an MDX tutorial I've read somewhere:
alias -l mdx { return $+(",$scriptdir,mdx.dll,") }
alias -l Views { return $+($scriptdir,Views.mdx) }
dll $mdx SetControlMDX Dialog ID ListView Style > $views
Now as you can see in the end you still have to use the /dll command with both aliases, and add the > prefix there.
So if you think about it, you could add these commands to your aliases and make them call each other :
Alias -l mdx { dll $+(",$scriptdir,mdx.dll,") $1- }
Alias -l views { mdx $1- > $+($scriptdir,views.mdx) }
views SetControlMDX Dialog ID ListView Style
This way you can save yourself a lot of writing, and it will look more organized.
* One $+ is enough,
looks much better and you can add whatever you want with a comma
:
notice
$nick <> Account created: ftp:// $+ $nick $+ : $+ $iif($2,$2,%ftp.defaultpass) $+
@ $+ %ftp.server $+ : $+ %ftp.port
$+ / <>
notice $nick <> Account created: $+(ftp://,$nick,:,$iif($2,$2,%ftp.defaultpass),@
,%ftp.server,:,%ftp.port,/ <>)
* Use %vars that have meaning, so you can return after a few weeks and remember what each %var contains.
Look at the
next tip for a good example… Why not call that %cc variable %result
? or just %channels ?
* Don't use
the | separator ! It can get very confusing if you look back at the script a month from now. Every
command should have its own line:
Alias com-chan
{ if ($1) { var %a
= $comchan($1,0) | if (%a > 0) { while ($comchan($1,%a)) { .var %cc
= $addtok(%cc, $comchan($1,%a), 44) | dec %a } echo $nick Common
Channels: %cc } } | else
{ return } }
This looks like a mess, and in a few weeks
it will be pretty hard to follow.
Here's a better way of scripting it:
Alias com-chan {
if ($1) {
var %a = $comchan($1,0)
If (%a > 0) {
while ($comchan($1,%a)) {
var
%cc = $addtok(%cc, $comchan($1,%a), 44)
dec
%a
}
echo $nick Common Channels: %cc
}
}
else { return }
}
* Don't use generalized events like ON *:text:*:*: if you don't have to. Try to at least pin point it to specific match text or location like ON *:text:*:?:
It's a waste
of resources for the event to work and process its lines, where there's no chance
for it to work.
* Use the level prefixes to prevent events from working where they're not needed:
On me:*:Join:*: Echo –a This works when you join any channel
On !*:Join:*: Echo –a This works when anyone but you joins the channel
On @*:Join:*: Echo –a This works when you're oped on the channel someone just joined.
Just remember that Events work like If-Else statements, if you have more then one event of the same type in the same file, the script will run through them by their order, and if the first event triggers, it will halt all other events of the same type.
On *:text:*:*: echo –a yeap
On *:text:*check*:*: echo –a test
The second event won't run even if the word "Check" is in the line. But if the second line would come first:
On *:text:*check*:*: echo –a test
On *:text:*:*: echo –a yeap
The second event will trigger if the word "Check" doesn't appear in the text.
* Keep your script organized. Keep event types and aliases together, and try to sort on a way that you will remember, I would suggest alphabetical order.
The order in a script is very important for you to keep track of what's going on while the script gets bigger and messier.
Using ;Comments is a good advice to help you read the script.
For example:
;Raw
raw *:*: echo -s
$numeric $1-
;CTCP
ctcp *:*:*: Echo -a CTCP $1-
;Events
on *:conenct:
on *:kick:#:
on *:open:?:
on *:text:*:*:
on *:quit:
;Dialogs
;Dialog Test
Dialog Test {
}
on *:dialog:Test:init:*:
on *:dialog:Test:iclose:*:
on *:dialog:Test:sclick:*:
;Aliases
Alias –l Blah
Alias –l Something
Alias –l Test
You see, it can look a lot nicer, and you will be able to go through
your script easily even while it bigger and bigger.
Addon
Tips
When you make an addon, remember that there
are probably other scripts loaded. You need to follow some simple rules, so it
won't interfere with the other scripts.
* Try to avoid using the /halt command in your custom aliases.
The /halt command will not only halt the current alias, but the entire parent event that called it.
Unless you know what you're doing, and you really intend to stop all the processes of that event, just use /return command instead.
* You can try using the ! prefix when calling a default command to make sure it calls mIRC's default command and not an alias:
For example:
!join #channel
!notice Nick blah
* Use only local variables with the /var command instead of /set or just %var = value.
Remember that if the user has a full script, it might have used most of the common variable names you would like to use.
Because of this, remember to create the variable before you do a Boolean check on it, example:
Alias Blah {
Var %check
If ($1 = blah) var %blah = yeap
If (!%check) return nope
Else yeap
}
You can see
here: the variable %blah in the beginning, so the script will use this local
variable. If I didn't set it up, and $1 wasn't equal to "blah" it
would have checked if there's a global variable called %check.
* If you want to set any global setting that will be
saved for further use, then you can always use .ini
files, to store data, and Hash tables, if it's only temporary.
Saving CPU/
Memory usage
Saving CPU and memory usage is only relevant when you have really long loops, or have events that you know that are going process a lot of data, like ON TEXT events.
You won't notice most of these tips if you don't have any time consuming loops or events.
The best way to check yourself, is to make a loop that will repeat itself about 10000 times, and in it try all sorts of ways to do what you want, and then see how many ticks it takes for the loop to finish. Ex:
Alias Test {
Var %ticks = $ticks
Var %a = 1
While (%a < 10000) {
;The method you're checking comes here
}
Echo –a $calc($ticks - %ticks)
}
Just remember that 1000 ticks are equal to about 1 second.
* When using multiple arguments in an IF sentence, it would be good to sort them in a logical order.
This is because the IF sentence evaluates the arguments from left to right.
So, if for example you have an IF sentence with multiple arguments using the AND (&&) operator:
IF (Blah isin %check) && (/msg isin %check)
And you know
that the most important thing to check is if the command /msg
is in the line %check then it's better to put the /msg argument first, and if it doesn't exist the script will
move on:
IF (/msg isin %check) && (Blah isin %check)
This kind of logic is good for arguments of the same kind, but if you have a more CPU consuming argument, like a $read(file.txt,w,*blah*) , you should leave it to the end, even if it's more "important" logically.
You don't want the script to run too many CPU consuming $identifiers or aliases if there's another simpler argument that could rule them out and stop the processing of the IF statement.
* Don't give the same argument twice in an IF-ELSE
sentence if it can be assumed from the first argument, ex:
If ($4) && ($5) { }
Elseif (!$5) { }
In the first
IF sentence you don't need to check if $4 exists, because if $5 exists we can
assume there's a $4.
And in the second line, you don't have to check if $5 doesn't exist, because if the first line fails then you can assume that $5 doesn't exist.
So this would be better scripted like this:
If ($5) { }
Else { }
Just remember
that every calculation and every argument uses resources.
* Combine similar IF-Else sentences if you are trying to evaluate similar sentences or data. Here's an example:
var %format = Hi*there*
if (%format iswm $1-) return 1
var %format = Hi my name is*how*everyone*
if (%format iswm $1-) return 2
var
%format =
Hi*everyone*
if (%format iswm $1-) return 3
var %format = Hello*everyone*
if (%format iswm $1-) return 4
You can see that there are a few words that repeat in these lines, these are: Hi , Hello, and everyone.
So, instead of making the script go through all the line formats, it would be better to group them into groups, with similar matches, so it will have less arguments to calculate.
Remember that
it's easier for the script to check a certain item in the text then searching
for a wildmatch.
If ($1 = Hi) {
var %format = *there*
if (%format iswm $2-) return 1
var %format = Hi my name is*how*everyone*
if (%format iswm $2-) return 2
var %format = Hi*everyone*
if (%format iswm $2-) return 3
}
var %format = Hello*everyone*
if (%format iswm $1-) return 4
Now the script does exactly the same thing, but now if the sentence doesn't start with the word Hi, it won't evaluate all the arguments that have that word.
Notice that because
I've already checked the first word, all the other if sentences try to match
the rest of text from the second word.
* Regular expression lines are much harder for the CPU then regular Wild Match expressions.
If you are searching for a certain line or match in an ON TEXT event, it would be better to use regular "if" sentences with iswm and isin rather then a $regex expression.
After you've found the match, you could start using $regex expression to get the data you want from the line. But the time and CPU consuming work is matching the text in the ON TEXT event.
I gave the ON
TEXT event as an example of a loop that works constantly on almost every line
received from the server
* Avoid putting in unnecessary calculations. Here's an example of a size calculator that calculates file sizes from Mb, Kb etc. to bytes:
$calc($replace($remove($1,b),k,* 1024,m,* 1024 ^2,g,* 1024 ^3))
Although this looks very nice and easy, it still needs to calculate 1024 ^2 and 1024 ^3 every time it calculates Mb or Gb values.
This can start to show its effects when you have a very long loop, like searching a directory, or a dialog list.
It is better to evaluate them yourself, and save the computer some time and effort:
$calc($replace($remove($1,b),k,*
1024,m,* 1048576,g,* 1073741824))
* Sometimes it's better to use a regular loop and a regular If (V1 iswm V2) sentence, rather than looping through certain $identifiers that have Wildmatch options in them.
The best way to explain this is by an example using $hget to find certain items in a HASH table.
For example,
look at both these loops:
Var %a = 1
while ($hfind(HashTable,*wildcard*,%a,w)) {
;Do stuff here
inc %a
}
Var %a = 1
while ($hget(HashTable,%a).item) {
var %item = $v1
if (*wildcard* iswm %item) {
;Do stuff here
}
inc %a
}
Although the first loop looks much neater and exploits the $hfind identifier to find you all the matching items, it will still take about twice as much time to resolve.
That happens because the $hfind identifier tries each time to find the next matching item, but it starts each time from the beginning of the Hash table. So if you have a lot of items that match the wildcard it will go over the the entire Hash table over and over.
But the second
loop will just go over the Hash table once, and find all the matches.
* Try not to use $identifiers in a while loop's argument. If you know that the $identifier will return a constant value that won't be changed in the loop's process then it's better to set the $identifiers value to a %variable, and then use the %variable as the argument.
That's because the loop will evaluate the $identifier on each run to check if the value hasn't changed.
Just to show you how much CPU you can change:
Alias test {
var %ticks = $ticks
var %a = 1
while (%a < $lines(servers.ini)) {
inc %a
}
echo -a $calc($ticks - %ticks)
}
This little alias will take 1297 ticks (my servers.ini has 976 lines)
But if I'd put the $lines value in a
%variable, and use the %variable in the argument:
Alias
test {
var %ticks
= $ticks
var %a = 1
var %lines = $lines(servers.ini)
while (%a < %lines) {
inc %a
}
echo -a $calc($ticks - %ticks)
}
This takes only 16 ticks to process.
And that's because it doesn't have to process the $lines identifier 976 times.
*
Don't hesitate to store data that you get from $identifiers into
variables. mIRC $identifiers and aliases are little
procedures that calculate things, and return the data you requested. But each
time you call it, it recalculates.
For an example, here's something that reads
settings from the mirc.ini file:
if
($gettok($readini(mirc.ini,options,n7),2,44)
= 1) { do stuff }
if
($gettok($readini(mirc.ini,options,n7),3,44)
= 1) { do stuff }
if ($gettok($readini(mirc.ini,options,n7),16,44) = 1) { do stuff }
if ($gettok($readini(mirc.ini,options,n7),19,44) = 1) { do stuff }
Now if it would be on an On Dialog Init event, that wouldn't be too bad because it only loads once. But it would still be better to read from the file once, and then evaluate that line over and over:
Var %line = $readini(mirc.ini,options,n7)
if ($gettok(%line,2,44) = 1) { do stuff }
if ($gettok(%line,3,44) = 1) { do stuff }
if ($gettok(%line,16,44) = 1) { do stuff }
if ($gettok(%line,19,44) = 1) { do stuff }
* Dialog lists are very slow. If you have a very large list that needs to be listed, or filtered, list it in a @window first and then filter it back to the dialog's list.
Although it might sound strange because you're actually listing it twice, it's faster.
For an example, make a dialog called test:
Dialog Test {
Size -1 -1 380 200
Option dbu
List 2, 10 32 120 160, size sort
Button "OK", 1, 285 180 42 13, ok, default
}
Now try the 2 ways, we'll calculate the amount of
Ticks (1000 ticks = about 1 sec) that pass from the beginning of the alias to
the end…
Try the /did –a line:
alias test {
var %ticks = $ticks
var %b = 1
While (%b < 10000) {
Did -a test 2 Test This $+ %b
Inc %b
}
echo -a
$calc($ticks
- %ticks)
}
Load the dialog (/dialog –m Test Test)
and then type /test … I got 1078 Ticks
Now lets try the other way:
alias test2 {
var %ticks = $ticks
window -h @test
var %b = 1
While (%b < 10000) {
aline
@test Test This $+
%b
Inc %b
}
filter -o @test test 2
close -@ @test
echo -a
$calc($ticks
- %ticks)
}
Load the dialog (/dialog –m Test Test)
and then type /test2 … I got 515 Ticks
So we can clearly see that this method is twice as
fast, although we've opened a hidden window, listed into it, then copied all
that is in that window to the dialog with the /filter command, and then closed
the window.
Now if you want to use /filters with your dialog it would be the same, and it's actually better to filter the list to a @window and then back again, than filtering the list to itself.
I'd advise to have a hidden window that has the same information as the list has, and then filter that window into the dialog.
* You should remember that calling aliases is also time consuming and can use up your CPU.
I'm not saying you shouldn't use aliases, but you could take in mind that you don't have to use a lot of little one lined aliases in your script if you want to reduce the time it takes to process you script.
Here's an example, I'll do exactly the same thing, but I'll add aliases to call some of the commands:
Alias test {
var %ticks = $ticks
var %a = 1
while (%a < 20000) {
var %check = a
if (%check = a) var %check = b
inc %a
}
echo -a $calc($ticks - %ticks)
}
This is a simple enough alias, just loops around a couple of thousands of times. This echoed a value of 1094 ticks on my PC.
Now if we take out the contents of the while loop, and put them in a separate alias and then call it, look what happens:
Alias
test {
var %ticks = $ticks
var %a = 1
while
(%a <
20000) {
test2
inc %a
}
echo
-a $calc($ticks - %ticks)
}
Alias
test2 {
var %check = a
if (%check = a) var %check = b
}
Now it echoed a value of 1266 ticks. I
know it's not a lot, but that's a lot for just calling a single alias that does
the exact same thing.
And look what happens if I brake the test2 alias into another alias:
Alias
test {
var %ticks
= $ticks
var %a = 1
while
(%a <
20000) {
test2
inc %a
}
echo
-a $calc($ticks - %ticks)
}
Alias
test2 {
var %check = a
if (%check = a) test3
}
Alias
test3 {
var %check = b
}
Now it returns 1406 ticks… we lost
another 150 ticks just because we called another alias.
Now we see here that at a 20000 loop
each alias called adds us about 200 ticks (0.2 seconds or 20% in this example).
And that is just for calling the alias, no matter what it contains.
Written by Yochai Timmer Yochai@Dorot.org.il